﻿using System;
using System.IO;
using NUnit.Framework;

namespace Reflective.Tests
{
    [TestFixture]
    public class ILGeneratorExtensionsTests
    {
        [Test]
        public void AddTwoNumbers()
        {
            var method = new DynamicMethod<Func<int, int, int>>();
            method.IL
                .ldargs(0, 1)
                .add()
                .ret();
            Func<int, int, int> func = method.CreateDelegate();

            int result = func(17, 42);

            Assert.That(result, Is.EqualTo(59));
        }

        [Test]
        public void AddTwoConstants()
        {
            var method = new DynamicMethod<Func<int>>();
            method.IL
                .ldc_i4_1()
                .ldc_i4_opt(3)
                .add()
                .ret();
            Func<int> func = method.CreateDelegate();

            int result = func();

            Assert.That(result, Is.EqualTo(4));
        }

        public class TestClass : IDisposable
        {
            public static int DisposedCount;

            public void Dispose()
            {
                DisposedCount++;
            }

            public void ThrowIOException()
            {
                throw new IOException("123");
            }
        }

        [Test]
        public void UsingTest()
        {
            var method = new DynamicMethod<Action>();

            method.IL
                .newobj<TestClass>()
                .EmitUsing(il => il)
                .ret();

            TestClass.DisposedCount = 0;
            method.CreateDelegate()();

            Assert.That(TestClass.DisposedCount, Is.EqualTo(1));
        }

        [Test]
        public void TryFinallyTest()
        {
            var method = new DynamicMethod<Func<string>>();

            method.IL
                .variable<string>() //                              string temp = "no finally";
                .ldstr("no finally")
                .stloc_0()
                .EmitTry(il2 => il2 //                              try {
                    .EmitTry(il => il //                                try {
                        .ldstr("123") //                                    throw new IOException("123");
                        .newobj<IOException>(typeof(string))
                        .throw_()
                        .EmitFinally(f => f //                          } finally {
                            .ldstr("finally") //                            temp = "finally";
                            .stloc_0())) //                             }
                    .EmitCatch<IOException>((c, lbl) => c)) //      } catch (IOException) { }
                .ldloc_0() //                                       return temp;
                .ret();

            Func<string> func = method.CreateDelegate();
            string output = func();

            Assert.That(output, Is.EqualTo("finally"));
        }
    }
}